package uu.tools.core;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.*;

public class StringFormatter {
    private static ObjectMapper objectMapper = new ObjectMapper();

    public static String format(String origin, Object... maps) {
        Map<Object, Object> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        if (maps != null) {
            for (Object o : maps) {
                if (o instanceof Map m) {
                    set(m, map, "");
                } else if (o instanceof JsonNode jn) {
                    set(jn, map, "");
                } else if (o instanceof Iterable<?> i) {
                    set(i, map, "");
                } else {
                    try {
                        set(objectMapper.convertValue(o, Map.class), map, "");
                    } catch (Exception e) {
                        // ignore
                    }
                }
            }
        }
        format(origin.toCharArray(), sb, map);
        return sb.toString();
    }

    public static void set(Iterable iterable, Map map, String parent) {
        int i = 0;
        Iterator iterator = iterable.iterator();
        while (iterator.hasNext()) {
            Object v = iterator.next();
            String key = parent + (parent.isEmpty() ? "" : ".") + i++;
            if (v instanceof Map n) {
                set(n, map, key);
            } else if (v instanceof Iterable n) {
                set(n, map, key);
            } else if (v instanceof Float f && isInteger(f)) {
                map.put(key, String.valueOf(f.longValue()));
            } else if (v instanceof Double d && isInteger(d)) {
                map.put(key, String.valueOf(d.longValue()));
            } else if (v instanceof String || v instanceof Number || v instanceof Boolean) {
                map.put(key, v.toString());
            }
        }
    }

    public static void set(Map<?, ?> m, Map map, String parent) {
        for (Map.Entry<?, ?> entry : m.entrySet()) {
            Object v = entry.getValue();
            String key = parent + (parent.isEmpty() ? "" : ".") + entry.getKey();
            if (v instanceof Map n) {
                set(n, map, key);
            } else if (v instanceof Iterable n) {
                set(n, map, key);
            } else if (v instanceof Float f && isInteger(f)) {
                map.put(key, String.valueOf(f.longValue()));
            } else if (v instanceof Double d && isInteger(d)) {
                map.put(key, String.valueOf(d.longValue()));
            } else if (v instanceof String || v instanceof Number || v instanceof Boolean) {
                map.put(key, v.toString());
            }
        }
    }

    private static void set(JsonNode jsonNode, Map<Object, Object> map, String parent) {
        if (jsonNode.isObject()) {
            Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> next = fields.next();
                String k = parent + (parent.isEmpty() ? "" : ".") + next.getKey();
                JsonNode v = next.getValue();
                if (v.isObject() || v.isArray()) {
                    set(v, map, k);
                } else {
                    map.put(k, v.asText());
                }
            }
        } else if (jsonNode.isArray()) {
            int i = 0;
            for (JsonNode node : jsonNode) {
                String k = parent + (parent.isEmpty() ? "" : ".") + i++;
                if (node.isObject() || node.isArray()) {
                    set(node, map, k);
                } else {
                    map.put(k, node.asText());
                }
            }
        }
    }

    private static void format(char[] chars, StringBuilder result, Map<?, ?> map) {
        StringBuilder key = new StringBuilder();
        StringBuilder escape = new StringBuilder();
        for (char c : chars) {
            if (c == '\\') {
                escape.append(c);
            } else if (escape.length() > 0 && c != '\\') {
                if (c == '{') {
                    escape.setLength(escape.length() - 1);
                }
                result.append(escape);
                result.append(c);
                escape.setLength(0);
            } else if (c == '{') {
                key.append(c);
            } else if (!key.isEmpty()) {
                key.append(c);
                if (c == '}') {
                    String k = key.toString().substring(1, key.length() - 1);
                    result.append(Objects.toString(map.get(k), ""));
                    key.setLength(0);
                }
            } else {
                result.append(c);
            }
        }
        if (!key.isEmpty()) {
            result.append(key);
        } else if (!escape.isEmpty()) {
            result.append(escape);
        }
    }

    public static boolean isInteger(double num) {
        return num == (long)num;
    }

    public static boolean isInteger(float num) {
        return num == (long)num;
    }
}
